#include <GameEngine/UnitTest/unit_test.h>

#include <GameEngine/Geometry/Core/angle.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif


//! Launch the unit tests on Angle objects
/*!
 @return True if all unit tests on Angle objects were
         succesful, false otherwise
 */
template <class T>
bool UnitTest::test_geometry_angle_functions() {
	bool res = true;

	// Get number of bytes
	MemoryManager::BytesCount bytes = MemoryManager::instance()->bytes();

	// Create angle
	GameEngine::Angle<T> angle((T)90.0, GameEngine::Angles::Deg);

	// Test conversions
	T tolerance = (T)0.00001;
	{{
		res &= UNIT_TEST_ERROR( "[Angle] Test deg()", ( abs(angle.deg()-(T)90.0) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test rad()", ( abs(angle.rad()-(T)(PI/2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test grad()", ( abs(angle.grad()-(T)100.0) < tolerance ) );
	}}

	// Test assignment operator
	{{
		angle = GameEngine::Angle<T>((T)300.0, GameEngine::Angles::Grad);
		res &= UNIT_TEST_ERROR( "[Angle] Test deg()", ( abs(angle.deg()-(T)270.0) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test rad()", ( abs(angle.rad()-(T)(3.0*PI/2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test grad()", ( abs(angle.grad()-(T)300.0) < tolerance ) );
	}}

	// Test minus operator
	{{
		angle = angle - GameEngine::Angle<T>((T)200.0, GameEngine::Angles::Grad);
		res &= UNIT_TEST_ERROR( "[Angle] Test operator-()", ( abs(angle.deg()-(T)90.0) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator-()", ( abs(angle.rad()-(T)(PI/2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator-()", ( abs(angle.grad()-(T)100.0) < tolerance ) );
	}}

	// Test sum operator
	{{
		angle = angle + GameEngine::Angle<T>((T)200.0, GameEngine::Angles::Grad);
		res &= UNIT_TEST_ERROR( "[Angle] Test operator+()", ( abs(angle.deg()-(T)270.0) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator+()", ( abs(angle.rad()-(T)(3.0*PI/2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator+()", ( abs(angle.grad()-(T)300.0) < tolerance ) );
	}}

	// Test minus operator
	{{
		angle -= GameEngine::Angle<T>((T)200.0, GameEngine::Angles::Grad);
		res &= UNIT_TEST_ERROR( "[Angle] Test operator-=()", ( abs(angle.deg()-(T)90.0) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator-=()", ( abs(angle.rad()-(T)(PI/2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator-=()", ( abs(angle.grad()-(T)100.0) < tolerance ) );
	}}

	// Test sum operator
	{{
		angle += GameEngine::Angle<T>((T)200.0, GameEngine::Angles::Grad);
		res &= UNIT_TEST_ERROR( "[Angle] Test operator+=()", ( abs(angle.deg()-(T)270.0) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator+=()", ( abs(angle.rad()-(T)(3.0*PI/2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator+=()", ( abs(angle.grad()-(T)300.0) < tolerance ) );
	}}

	// Test division operator
	{{
		angle = angle / (T)3.0;
		res &= UNIT_TEST_ERROR( "[Angle] Test operator/()", ( abs(angle.deg()-(T)90.0) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator/()", ( abs(angle.rad()-(T)(PI/2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator/()", ( abs(angle.grad()-(T)100.0) < tolerance ) );
	}}

	// Test multiplication operator
	{{
		angle = angle * (T)3.0;
		res &= UNIT_TEST_ERROR( "[Angle] Test operator*()", ( abs(angle.deg()-(T)270.0) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator*()", ( abs(angle.rad()-(T)(3.0*PI/2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator*()", ( abs(angle.grad()-(T)300.0) < tolerance ) );
	}}

	// Test division operator
	{{
		angle /= (T)3.0;
		res &= UNIT_TEST_ERROR( "[Angle] Test operator/=()", ( abs(angle.deg()-(T)90.0) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator/=()", ( abs(angle.rad()-(T)(PI/2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator/=()", ( abs(angle.grad()-(T)100.0) < tolerance ) );
	}}

	// Test multiplication operator
	{{
		angle *= (T)3.0;
		res &= UNIT_TEST_ERROR( "[Angle] Test operator*=()", ( abs(angle.deg()-(T)270.0) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator*=()", ( abs(angle.rad()-(T)(3.0*PI/2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test operator*=()", ( abs(angle.grad()-(T)300.0) < tolerance ) );
	}}

	// Test angle accessor
	{{
		res &= UNIT_TEST_ERROR( "[Angle] Test angle()", ( abs(angle.deg()-angle.angle(GameEngine::Angles::Deg)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test angle()", ( abs(angle.rad()-angle.angle(GameEngine::Angles::Rad)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test angle()", ( abs(angle.grad()-angle.angle(GameEngine::Angles::Grad)) < tolerance ) );
	}}

	// Test set operators
	{{
		angle.set_angle((T)90.0, GameEngine::Angles::Deg);
		res &= UNIT_TEST_ERROR( "[Angle] Test set_angle()", ( abs(angle.deg()-(T)90.0) < tolerance ) );
		angle.set_angle((T)200.0, GameEngine::Angles::Grad);
		res &= UNIT_TEST_ERROR( "[Angle] Test set_angle()", ( abs(angle.deg()-(T)180.0) < tolerance ) );
		angle.set_angle((T)(3.0*PI/2.0), GameEngine::Angles::Rad);
		res &= UNIT_TEST_ERROR( "[Angle] Test set_angle()", ( abs(angle.deg()-(T)270.0) < tolerance ) );
		angle.set_deg((T)90.0);
		res &= UNIT_TEST_ERROR( "[Angle] Test set_deg()", ( abs(angle.deg()-(T)90.0) < tolerance ) );
		angle.set_grad((T)200.0);
		res &= UNIT_TEST_ERROR( "[Angle] Test set_grad()", ( abs(angle.deg()-(T)180.0) < tolerance ) );
		angle.set_rad((T)(3.0*PI/2.0));
		res &= UNIT_TEST_ERROR( "[Angle] Test set_rad()", ( abs(angle.deg()-(T)270.0) < tolerance ) );
	}}

	// Test reduce angle
	{{
		angle.set_angle((T)380.0, GameEngine::Angles::Deg);
		angle.reduce_angle();
		res &= UNIT_TEST_ERROR( "[Angle] Test reduce_angle()", ( abs(angle.deg()-(T)20.0) < tolerance ) );
	}}

	// Test angle from 2D points
	{{
		GameEngine::Point2d<T> p0(1,0);
		GameEngine::Point2d<T> p1(0,0);
		GameEngine::Point2d<T> p2(0,1);
		GameEngine::Angle<T> a1 = GameEngine::Angle<T>::from_points(p0,p1,p2);
		GameEngine::Angle<T> a2 = GameEngine::Angle<T>::from_points(p1,p2,p0);
		res &= UNIT_TEST_ERROR( "[Angle] Test from_points(Point2d)", ( abs(a1.deg()-(T)90) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test from_points(Point2d)", ( abs(a2.deg()-(T)45) < tolerance ) );
	}}

	// Test angle from 3D points
	{{
		GameEngine::Point3d<T> p0(1,0,0);
		GameEngine::Point3d<T> p1(0,0,0);
		GameEngine::Point3d<T> p2(0,1,0);
		GameEngine::Angle<T> a1 = GameEngine::Angle<T>::from_points(p0,p1,p2);
		GameEngine::Angle<T> a2 = GameEngine::Angle<T>::from_points(p1,p2,p0);
		res &= UNIT_TEST_ERROR( "[Angle] Test from_points(Point3d)", ( abs(a1.deg()-(T)90) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Angle] Test from_points(Point3d)", ( abs(a2.deg()-(T)45) < tolerance ) );
	}}

	// Get number of bytes
	res &= UNIT_TEST_ERROR( "[Angle] Test memory leak", ( bytes == MemoryManager::instance()->bytes() ));

	// Return result
	return res;
}


#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
